from __future__ import division

import math

import angles
from collision import Rect

def AngleVector(angle, magnitude=1):
	return Vector(magnitude * math.cos(angle), magnitude * math.sin(angle))

class Vector(object):
	"""A vector with x, y, and z magnitudes that can be multiplied by a scalar
	or have another vector added to it."""
	
	def __init__(self, x, y=0):
		self.x = x
		self.y = y
	
	def __eq__(self, vector):
		return (self.x == vector.x and self.y == vector.y)
	
	def __add__(self, add):		
		return Vector(self.x + add.x, self.y + add.y)
		
	def __radd__(self, add):
		return self.__add__(add)
	
	def __iadd__(self, add):
		self.x += add.x
		self.y += add.y
		
		return self

	def __sub__(self, sub):		
		return Vector(self.x - sub.x, self.y - sub.y)
	
	def __rsub__(self, sub):
		return Vector(sub.x - self.x, sub.y - sub.y)
	
	def __isub__(self, sub):
		self.x -= sub.x
		self.y -= sub.y
		
		return self
	
	def __mul__(self, mul):
		return Vector(self.x * mul, self.y * mul)
		
	def __rmul__(self, mul):
		return self.__mul__(mul)
		
	def __imul__(self, mul):
		self.x *= mul.x
		self.y *= mul.y
		
		return self
		
	def __div__(self, div):
		return Vector(self.x / div, self.y / div)
		
	def __rdiv__(self, div):
		raise NotImplementedError
	
	def __idiv__(self, div):
		self.x /= div
		self.y /= div
	
	def dot(self, vector):
		return self.x * vector.x + self.y * vector.y
	
	def perp(self, vector):
		return self.x * vector.y - self.y * vector.x

	def star(self, vector):
		return Vector(self.x * vector.x, self.y * vector.y)
	
	def magnitude(self):
		return math.sqrt(self.dot(self))
	
	def angle(self):
		return ORIGIN.angle_to_point(self)
	
	def normalised(self):
		return self / self.magnitude()
	
	def rotated(self, angle):
		angle = angles.clamp_angle(self.angle() + angle)
		
		return AngleVector(angle, self.magnitude())
	
	def rotate(self, angle):
		angle = angles.clamp_angle(self.angle() + angle)
		
		self.x, self.y = AngleVector(angle, self.magnitude())
		
		return self
	
	def angle_to_point(self, point):
		difference = point - self
		angle = math.atan2(difference.y, difference.x)
		
		return angles.clamp_angle(angle)
	
	def angle_between_vector(self, vector):
		mag1 = self.magnitude()
		mag2 = vector.magnitude()
		
		if 0 in (mag1, mag2):
			return 0
		else:
			return self.dot(vector) / (self.magnitude() * vector.magnitude())
	
	def distance(self, object):
		if isinstance(object, Vector):
			return self.distance_to_point(object)
		elif isinstance(object, Line):
			return self.distance_to_line(object)
		elif isinstance(object, Ray):
			return self.distance_to_ray(object)
		elif isinstance(object, Segment):
			return self.distance_to_segment(object)
	
	def distance_to_point(self, point):
		return (self - point).magnitude()
	
	def distance_to_segment(self, segment):
		"""Returns the distance from a segment to a point."""	
		v = segment.end - segment.start
		w = self - segment.start
		
		# Are the endpoints closest?
		c1 = w.dot(v)
		c2 = v.dot(v)
		
		if c1 <= 0:
			return self.distance_to_point(segment.start)
		elif c2 <= c1:
			return self.distance_to_point(segment.end)
		else:
			# The line is closest.
			b = c1 / c2
			point_b = segment.start + v * b
			
			return self.distance(point_b)
	
	def distance_to_ray(self, ray):
		#raise NotImplementedError
		v = segment.end - segment.start
		w = self - segment.start
		
		# Are the endpoints closest?
		c1 = w.dot(v)
		c2 = v.dot(v)
		
		if c1 <= 0:
			return self.distance_to_point(segment.start)
		#elif c2 <= c1:
		#	return self.distance_to_point(segment.end)
		else:
			# The line is closest.
			b = c1 / c2
			point_b = segment.start + v * b
			
			return self.distance(point_b)
	
	def distance_to_line(self, line):
		v = line.end - line.start
		w = self - line.start
		
		c1 = w.dot(v)
		c2 = v.dot(v)
		
		b = c1 / c2
		
		point_b = line.start + v * b
		
		return self.distance_to_point(point_b)
	
	def copy(self):
		return Vector(self.x, self.y)
	
	def __copy__(self):
		return self.copy()
	
	def __getitem__(self, index):
		return (self.x, self.y)[index]
	
	def __iter__(self):
		return iter((self.x, self.y))

HORIZONTAL = Vector(1, 0)

class Line(object):
	"""A line that passes through and extends infinitely beyond both the points
	'start' and 'end'"""
	
	def __init__(self, start, end):
		self.start = start
		self.end = end
	
	def segment(self, clip):
		raise NotImplementedError
	
	def rect(self, clip):
		return self.segment.rect()
	
	def collideline(self, line):
		raise NotImplementedError
	
	def collideray(self, ray):
		raise NotImplementedError
	
	def collidesegment(self, segment):
		raise NotImplementedError
	
class Ray(Line):
	"""A ray that starts at the point 'start', and extends infinitely through 
	and beyound the point 'end'"""
	
	def segment(self, clip):
		raise NotImplementedError
	
	def rect(self, clip):
		return self.segment.rect()
	
	def collideline(self, line):
		raise NotImplementedError
	
	def collideray(self, ray):
		raise NotImplementedError
	
	def collidesegment(self, segment):
		raise NotImplementedError
	
class Segment(Line):
	"""A line segment that goes between the points 'start' and 'end'"""
	
	def segment(self, clip):
		return self
	
	def rect(self, clip):
		x = min(self.start.x, self.end.x)
		y = min(self.start.y, self.end.y)
		
		width = max(self.start.x, self.end.x) - x
		height = max(self.start.y, self.end.y) - y
		
		return Rect(x, y, width, height)
	
	def collideline(self, line):
		raise NotImplementedError
	
	def collideray(self, ray):
		raise NotImplementedError
	
	def collidesegment(self, segment):
		raise NotImplementedError

ORIGIN = Vector(0, 0)
